#include <string>
#include <iostream>
#include <cmath> 

// For terminal delay
#include <chrono>
#include <thread>

#include <fstream>
#include <algorithm>

#include "game.h"
using std::string;
using std::vector;

void mGameWidget::setHead(SnakeHead h){
    mHead = h;
}

void mGameWidget::initsnake(){
    this->mPtrSnake.reset(new Snake(700, 500, 20));
}

void mGameWidget::moveToMouse(int x, int y){
    double hx = mPtrSnake->getSnake()[0].getX();
    double hy = mPtrSnake->getSnake()[0].getY();
    if (x != int(hx) && y != int(hy)){
        double distance = sqrt(pow(hx - x, 2) + pow(hy - y, 2));
        double dx = (x - hx) / distance;
        double dy = (y - hy) / distance;

    }
}

void mGameWidget::paintEvent(QPaintEvent* event){
    Q_UNUSED(event);
    painter.begin(this);
    QRect rect(mPtrSnake->getSnake()[0].getX(), mPtrSnake->getSnake()[0].getY(), BodyWidth, BodyHeight);
    string imp = mHead.getImage();
    int is = imp.size();
    imp = imp.substr(11, is - 13);
    painter.drawImage(rect, QImage(QString(imp.c_str())));
    qDebug() << "PRINTED";
    painter.end();
}

void mGameWidget::mousePressEvent(QMouseEvent* e){
    if (e->button() == Qt::LeftButton){
        moveToMouse(e->position().x(), e->position().y());
    }
}

QString tr(const char* str){
    return QString(str);
}

void wrefresh(QWidget* qw){
    qw->show();
}

Game::Game(QWidget* q, SnakeHead sh): QWidget(q), mSnakeHead(sh)
{
    this->setParent(q);
    this->mScreenHeight = q->height();
    this->mScreenWidth = q->width();
    this->setGeometry(0, 0, mScreenWidth, mScreenHeight);
    this->setStyleSheet("background-color: rgb(220, 255, 220);");
    this->mGameBoardWidth = this->mScreenWidth - this->mInstructionWidth;
    this->mGameBoardHeight = this->mScreenHeight - this->mInformationHeight;

    this->mInformationBoard = new QWidget(this);
    this->mInformationBoard->setGeometry(0, 0, 800, 100);
    this->mInformationBoard->setStyleSheet("background-color: rgb(240, 240, 240);");

    this->mGameBoard = new mGameWidget(this);
    this->mGameBoard->setGeometry(0, 100, 700, 500);
    this->mGameBoard->setStyleSheet("background-color: rgb(250, 250, 250);");

    // Initialize the leader board to be all zeros
    this->mLeaderBoard.assign(this->mNumLeaders, 0);
    qDebug() << "Constructed";
}

Game::~Game()
{
    delete mInformationBoard;
    delete mGameBoard;
}

QSize Game::sizeHint() const{
    return QSize(800, 600);
}

int Game::renderRestartMenu() const
{
    QMessageBox* menu;
    int width = this->mGameBoardWidth * 0.5;
    int height = this->mGameBoardHeight * 0.5;
    int startX = this->mGameBoardWidth * 0.25;
    int startY = this->mGameBoardHeight * 0.25 + this->mInformationHeight;

    menu = new QMessageBox(mParentWidget);
    menu->setGeometry(startX, startY, width, height);
    menu->setWindowTitle("Game Over!");
    menu->setText("Do you want to continue for higher record?");
    vector<string> menuItems = {"Restart", "Continue", "Quit"};
    for (int i = 0; i < 3; i++){
        menu->addButton(QString(menuItems[i].c_str()), QMessageBox::ActionRole);
    }

    int ret = menu->exec();
    delete menu;
    return ret;
}

void Game::renderPoints() const
{
    std::string pointString = std::to_string(this->mPoints);

    wrefresh(mInformationBoard);
}

void Game::renderDifficulty() const
{
    std::string difficultyString = std::to_string(this->mDifficulty);

    wrefresh(mInformationBoard);
}

void Game::initializeGame()
{
    this->mGameBoard->initsnake();
    this->mGameBoard->setHead(this->mSnakeHead);
    this->createRamdonFood();
    this->mDifficulty = 0;
    this->mPoints = 0;
    this->mDelay = this->mBaseDelay;
}

void Game::createRamdonFood()
{
    std::vector<SnakeBody> availableGrids;
    for (int i = 1; i < this->mGameBoardHeight - 1; i ++)
    {
        for (int j = 1; j < this->mGameBoardWidth - 1; j ++)
        {
            if(this->mGameBoard->mPtrSnake->isPartOfSnake(j, i))
            {
                continue;
            }
            else
            {
                availableGrids.push_back(SnakeBody(j, i));
            }
        }
    }

    // Randomly select a grid that is not occupied by the snake
    int random_idx = std::rand() % availableGrids.size();
    this->mFood = availableGrids[random_idx];
}

void Game::startGame()
{
    /*
    this->show();
    this->initializeGame();
    std::this_thread::sleep_for(std::chrono::milliseconds(3000));
    */
    this->initializeGame();
    this->show();

    qDebug() << "Slot called";

}

// https://en.cppreference.com/w/cpp/io/basic_fstream
bool Game::readLeaderBoard()
{
    std::fstream fhand(this->mRecordBoardFilePath, fhand.binary | fhand.in);
    if (!fhand.is_open())
    {
        return false;
    }
    int temp;
    int i = 0;
    while ((!fhand.eof()) && (i < mNumLeaders))
    {
        fhand.read(reinterpret_cast<char*>(&temp), sizeof(temp));
        this->mLeaderBoard[i] = temp;
        i ++;
    }
    fhand.close();
    return true;
}

bool Game::updateLeaderBoard()
{
    bool updated = false;
    int newScore = this->mPoints;
    for (int i = 0; i < this->mNumLeaders; i ++)
    {
        if (this->mLeaderBoard[i] >= this->mPoints)
        {
            continue;
        }
        int oldScore = this->mLeaderBoard[i];
        this->mLeaderBoard[i] = newScore;
        newScore = oldScore;
        updated = true;
    }
    return updated;
}

bool Game::writeLeaderBoard()
{
    // trunc: clear the data file
    std::fstream fhand(this->mRecordBoardFilePath, fhand.binary | fhand.trunc | fhand.out);
    if (!fhand.is_open())
    {
        return false;
    }
    for (int i = 0; i < this->mNumLeaders; i ++)
    {
        fhand.write(reinterpret_cast<char*>(&this->mLeaderBoard[i]), sizeof(this->mLeaderBoard[i]));;
    }
    fhand.close();
    return true;
}








